package com.fr.plugin.oauth2;

import com.fanruan.api.log.LogKit;
import com.fanruan.api.web.FlushKit;
import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider;
import com.fr.decision.webservice.v10.login.LoginService;
import com.fr.decision.webservice.v10.login.TokenResource;
import com.fr.general.http.HttpToolbox;
import com.fr.json.JSONObject;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.context.PluginContexts;
import com.fr.record.analyzer.EnableMetrics;
import com.fr.stable.StringUtils;
import com.fr.third.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;

/**
 * @author LSY
 * @version 10.0
 * Created by LSY on 2021-12-17
 **/
@EnableMetrics
public class OAuthLoginFilter extends AbstractGlobalRequestFilterProvider {
    private static final MyMap OAUTH_SERVICE = MyMap.getObj();
    private static final String COOKIE_AUTH = "oauth_access_token";
    private static final int TIME = 3600000 * 24;
    private static final String CALLBACK = "/oauth2/callback";
    private static final String DISABLE_OAUTH2 = "/oauth2/disable";
    private static String cpath = "";
    private static final String PLUGIN_ID = "com.fr.plugin.oauth2";
    private static final String HOME_PAGE = "/decision";

    @Override
    public void init(FilterConfig filterConfig) {
        FineLoggerFactory.getLogger().info(" FineLoggerFactory test -------- init!");
        mylog("---start OauthLogin Filter---OAUTH_SERVICE：- " + OAUTH_SERVICE);
        PluginSimpleConfig.getInstance();
        String loginUrl = PluginSimpleConfig.getInstance().getLoginUrl();
        mylog("------login url----:"+ loginUrl);
        if (PluginContexts.currentContext().isAvailable()) {
            // 做认证通过的事情
            cpath = filterConfig.getServletContext().getContextPath();
        } else {
            // 做认证未通过的事情
            cpath = "认证未通过";
        }
        mylog("-------------------- cpath:" + cpath);
    }

    @Override
    public String filterName() {
        FineLoggerFactory.getLogger().debug("-----filterName");
        return "OAuth Login Filter";
    }

    @Override
    public String[] urlPatterns() {
        FineLoggerFactory.getLogger().debug("-----urlPatterns");
        return new String[]{"/*"};
    }

    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
        mylog("-------------0026v---------------------doFilter----");
        PluginSimpleConfig psc = PluginSimpleConfig.getInstance();
        String uri = request.getRequestURI();
        mylog("----------------------------------uri----"+ uri);
        String loginUrl = psc.getLoginUrl();
        mylog("--------------------------------login url---"+ loginUrl);
        String query = request.getQueryString();
        if ((cpath+DISABLE_OAUTH2).equals(uri)){
            mylog(" DISABLE_OAUTH2 "+ uri+ ": query:" + query);
            request.getSession().setAttribute("STATUS_OAUTH2", "false");
            try{
                FineLoggerFactory.getLogger().debug("-----filterName");
                mylog("------------------------------------------errorDemo");
                response.sendRedirect(psc.getBaseUrl() + HOME_PAGE);
            }catch(Exception e){
                LogKit.error(e.getMessage(),e);
            }
            return;
        }
        String isis = (String)request.getSession().getAttribute("STATUS_OAUTH2");
        if (!psc.isEnabled() || "false".equals(isis)){
            mylog(" ---------- not enabled !----------");
            try {
                filterChain.doFilter(request, response);
            }catch (Exception e){
                MyException(e);
            }
            return;
        }

        String relogin = request.getParameter(psc.getForceReLogin());
        if ("true".equals(relogin)){
            goRedirectAuthorize(request,response);
            return;
        }


        if ((cpath+CALLBACK).equals(uri)){
            mylog(" callback"+ uri+ ": query:" + query);
            String code = getCode(request);
            try {
                mylog("-------- doOauth---------code:- " + code);
                doOauth(request, response, filterChain, code);
            } catch (Exception e) {
                MyException(e);
            }
            return;
        }
        if ((cpath+loginUrl).equals(uri)){
            mylog(" login url "+ uri+ ": query:" + query);
            // 判断 token 是否有效。 无效 重定向 // 暂时不判断。
            OauthLoginInfo loginInfo = getCurrentUser(request);
            if (loginInfo != null) {
                //如果oauth已经登录, 即已经存在本地会话，则刷新平台token
                mylog("-------- oauth! loginInfo != null && !\"true\".equals(relogin) ");
                refreshDecisionToken(request, response, loginInfo);
                try {
                    filterChain.doFilter(request, response);
                }catch (Exception e){
                    MyException(e);
                }
                return;
            } else {
                goRedirectAuthorize(request,response);
                return;
            }
        }
        try {
            filterChain.doFilter(request, response);
        }catch (Exception e){
            MyException(e);
        }
    }

    private void goRedirectAuthorize(HttpServletRequest request, HttpServletResponse response) {
        PluginSimpleConfig psc = PluginSimpleConfig.getInstance();
        String query = request.getQueryString();
        if (query != null){
            query = query.replace(psc.getForceReLogin(), "liusy");
        }else{
            query="";
        }
        String tmp = request.getRequestURL()+"?"+query;
        if (tmp.contains(psc.getLoginUrl()) || tmp.contains(CALLBACK) || tmp.contains("logout")){
            tmp = psc.getBaseUrl() + HOME_PAGE;
        }
        mylog("-------goRedirect-----url-----"+tmp);
        request.getSession().setAttribute("save_redirect", tmp);
        request.getSession().setAttribute("save_redirect_status", "has");
        try {
            response.sendRedirect(psc.getAuthorizeUrl() + "?client_id=" + psc.getClientId() + "&response_type=code"
                    + "&redirect_uri=" + psc.getBaseUrl()  + CALLBACK);
        }catch (Exception e){
            MyException(e);
        }


    }

    private void goRedirectHomeOrOldUrl(HttpServletRequest request, HttpServletResponse response) {
        String oldUrl = (String)request.getSession().getAttribute("save_redirect");
        String isHas = (String)request.getSession().getAttribute("save_redirect_status");
        PluginSimpleConfig psc = PluginSimpleConfig.getInstance();
        if ("has".equals(isHas) && oldUrl !=null && !oldUrl.isEmpty()) {
            request.getSession().setAttribute("save_redirect_status", "no");
            mylog("---------OK login " + oldUrl);
        }else {
            oldUrl = psc.getBaseUrl() + HOME_PAGE;
        }
        try {
            response.sendRedirect(oldUrl);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private void MyException(Exception e) {
        FineLoggerFactory.getLogger().error(e, e.getMessage());
        mylog("------------test ----------" + e.getMessage());
        e.printStackTrace();
    }

    private static OauthLoginInfo getCurrentUser(HttpServletRequest request) {
        Cookie cookie = WebUtils.getCookie(request, COOKIE_AUTH);
        mylog("----------------------------------getCurrentUser----");
        if (cookie == null) {
            return null;
        }
        mylog(" cookie.getValue() -------------------------"+cookie.getValue());
        try {
            return OAUTH_SERVICE.get(cookie.getValue());
        } catch (Exception e) {
            return null;
        }
    }


    private void refreshDecisionToken(HttpServletRequest request, HttpServletResponse response, OauthLoginInfo loginInfo) {
        mylog("----------------------------------refreshDecisionToken----");
        try {
            //String oldToken = TokenResource.COOKIE.getToken(request);
            //Device device = NetworkHelper.getDevice(request);
            LoginService.getInstance().loginStatusValid(request, TokenResource.COOKIE);
            //, TerminalHandler.getTerminal(request, device));
        } catch (Exception e) {
            try {
                String decisionToken = LoginService.getInstance().login(request, response, loginInfo.userName);
                request.setAttribute("fine_auth_token", decisionToken);
                mylog("   refresh " + decisionToken);
                OAUTH_SERVICE.put(loginInfo.accessToken, new OauthLoginInfo(loginInfo.userName, loginInfo.accessToken, decisionToken), TIME);
            } catch (Exception ex) {
                FineLoggerFactory.getLogger().error(e.getMessage(), e);
            }
        }
    }
    private String getCode(HttpServletRequest request) {
        return request.getParameter("code");
    }

    private void doOauth(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain, String code) throws IOException, ServletException {
        String accessToken = null;
        mylog("----------------------------------accessToken----");
        PluginSimpleConfig psc = PluginSimpleConfig.getInstance();
        try {
            String tmp = psc.getTokenUrl() + "?client_id=" + psc.getClientId()
                    + "&client_secret=" + psc.getClientSecret() + "&grant_type=authorization_code" + "&code=" + code
                    + "&redirect_uri=" + psc.getBaseUrl() + CALLBACK;
            // (1) 获取access_token
            JSONObject result = new JSONObject(HttpToolbox.post( tmp, null));
            if (result == null || result.isEmpty()){
                responseStr(request, response, "TokenUrl 无效！ TokenUrl 需要重新配置，当前是:"+ psc.getTokenUrl() + "");
                return;
            }
            accessToken = result.getString("access_token");
            mylog(" token : "+ accessToken);
            if (accessToken == null || accessToken.isEmpty()){
                responseStr(request, response, "TokenUrl 无效！accessToken =NULL，  TokenUrl 需要重新配置，当前是:"+ psc.getTokenUrl() + "");
                return;
            }
        } catch (IOException e) {
            FineLoggerFactory.getLogger().error(e.getMessage(), e);
            mylog("---------- "+ e.getMessage());
        }
        if (StringUtils.isNotEmpty(accessToken)) {
            // (2) 再根据accessToken去请求oauthUserInfoUrl得到用户名
            JSONObject userInfo = new JSONObject(HttpToolbox.get(psc.getUserInfoUrl() + "?client_id=" + psc.getClientId()
                    + "&access_token=" + accessToken+"&t="+new Date().getTime()));
            if (userInfo == null || userInfo.isEmpty()){
                responseStr(request, response, "UserInfoUrl 无效！ UserInfoUrl 需要重新配置，当前是:"+ psc.getUserInfoUrl() + "");
                return;
            }
            mylog(" user info "+userInfo.toString());
            String userName = userInfo.getString(psc.getUserName());
            if (userName == null || userName.isEmpty()){
                responseStr(request, response, "用户名称为空！ 用户字段需要重新配置，当前是:"+ psc.getUserName() + "");
                return;
            }
            // (3) 建立本地会话
            boolean x = storeLoginUser(request, response, userName, accessToken);
            if (x){
                goRedirectHomeOrOldUrl(request, response);
            }
        }else {
            // 如果获取不到, 说明code失效则重定向到oauth认证地址
            FineLoggerFactory.getLogger().error("oauth code is invalid");
            responseStr(request, response, " oauth code is invalid, info:" + request.getRequestURL());
        }
    }

    private static void responseStr(HttpServletRequest request, HttpServletResponse response, String str) {
        try{
            PluginSimpleConfig psc = PluginSimpleConfig.getInstance();
            FineLoggerFactory.getLogger().debug("-----responseStr");
            mylog("------------------------------------------responseStr");
            String t = psc.getBaseUrl() +"/oauth2/disable";
            str += "<a href='"+ t +"'>管理员登录（"+ t +"）</a>";
            FlushKit.printAsString(response,str);
        }catch(Exception e){
            LogKit.error(e.getMessage(),e);
        }
    }

    private boolean storeLoginUser(HttpServletRequest request, HttpServletResponse response, String userName, String accessToken) {
        mylog("----------------------------------storeLoginUser----");
        try {
            String decisionToken = LoginService.getInstance().login(request, response, userName);
            request.setAttribute("fine_auth_token", decisionToken);
            Cookie cookie = new Cookie(COOKIE_AUTH, accessToken);
            cookie.setPath("/");
            cookie.setHttpOnly(true);
            response.addCookie(cookie);
            OAUTH_SERVICE.put(accessToken, new OauthLoginInfo(userName, accessToken, decisionToken), TIME);
            return true;
        } catch (Exception e) {
            FineLoggerFactory.getLogger().error(e, e.getMessage());
            responseStr(request, response, e.getMessage() + ", 用户信息+"+ userName );
            return false;
        }
    }
    public static void deleteTicket(HttpServletRequest request, HttpServletResponse response){
        mylog("----------------------------------deleteTicket----");
        OauthLoginInfo loginInfo = getCurrentUser(request);
        if (loginInfo != null) {
            try {
                OAUTH_SERVICE.delete(loginInfo.accessToken);
            } catch (Exception e) {
                FineLoggerFactory.getLogger().error(e.getMessage(), e);
            }
            Cookie cookie = new Cookie(COOKIE_AUTH, "");
            cookie.setPath("/");
            cookie.setMaxAge(0);
            response.addCookie(cookie);
        }
    }
    public static void logout(HttpServletRequest request, HttpServletResponse response){
        mylog("-----------111-----------------------logout----");
        request.getSession().removeAttribute("STATUS_OAUTH2");
        PluginSimpleConfig psc = PluginSimpleConfig.getInstance();
        String reUrl = null;
        try {
            reUrl = URLEncoder.encode(psc.getBaseUrl()+HOME_PAGE, "UTF-8");
            mylog("-111---logout: "+ reUrl);
            String s;
            s = HttpToolbox.get(psc.getLogoutUrl()+"?response_type=code&redirect_uri="+ reUrl +"&client_id=" + psc.getClientId()+"&scope=user");
            mylog("Logout result: "+ s);
            if (s == null || s.isEmpty()){
                responseStr(request, response, "LogoutUrl 无效！ LogoutUrl 需要重新配置，当前是:"+ psc.getLogoutUrl() + "");
                return;
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private static void mylog(String s) {

    }

}
